<?php
/**
 * PrynLib
 * @copyright 2007-2009 Prynel
 * @author Olivier ROGER <roger.olivier@gmail.com>
 * @category libPrynel
 * @package File
 * @version $Revision: 120 $
 */

/**
 * Classe d'envoi de multiples fichiers.
 * Nécessite l'extension PECL fileinfo
 * 
 * @category libPrynel
 * @package File
 * @version 1.2.4 
 * @author Olivier ROGER <roger.olivier@gmail.com>
 * @copyright  2007-2009 Prynel
 * 
 *     
 * <code>
 * try{
 * $upload = new File_Upload('files','dossier/');
 * $upload->addExtension(array('swf','tar.gz'));
 * $upload->upload();
 * }
 * catch(Util_ExceptionHandler $e){
 * $e->getError()
 * </code>
 * @link http://pecl.php.net/package/Fileinfo
 */

class File_Upload
{
	const REQUIRE_ALL 		= 2;
	const REQUIRE_NO  		= 0;
	const REQUIRE_YES 		= 1;
	const WMODE_OVERWRITE 	= 0;
	const WMODE_COPY	  	= 1;
	const WMODE_CANCEL	  	= 2;
	
	/**
	 * Taille maximale autorisée en Ko
	 * @access public
	 * @var int
	 */
	
	public $maxFileSize;
	/**
	 * Dossier d'upload. Doit se terminer par un /
	 * @access public
	 * @var string
	 */
	public $dirUpload;
	
	/**
	 * nom du champs de l'input file
	 * @access private
	 * @var string
	 */
	private $fieldName;
	
	/**
	 * Nom de fichier (optionnel)
	 * @access public
	 * @var string
	 */
	public $fileName;
	
	/**
	 * Préfixe à ajouter au nom du fichier (optionnel)
	 * @access public
	 * @var string
	 */
	public $prefixe;
	
	/**
	 * Suffixe à ajouter au nom de fichier (optionnel)
	 * @access public
	 * @var string
	 */
	public $suffixe;
	
	/**
	 * Définis si les champs sont obligatoire
	 * REQUIRE_ALL = tous obligatoire
	 * REQUIRE_YES = seul le premier
	 * REQUIRE_NO  = Pas obligatoire
	 * @access public
	 * @var int
	 */
	public $required;
	
	/**
	 * Tableau des type mime refusés lors de l'upload
	 * @access public
	 * @since 1.1.0
	 * @var array
	 */
    public $authorizedMime = array('image/gif','image/jpeg','image/png','text/plain');
	
	/**
	 * Mode d'écriture du fichier
	 * WMODE_OVERWRITE = Ecrase si existe
	 * WMODE_COPY 	   = renomme le nouveau en Copie_de_xxx si existe
	 * WMODE_CANCEL    = Annule l'upload si existe
	 * @access public
	 * @var int
	 */
	public $writeMode;
	
	/**
	 * Tableau des extensions autorisées de base
	 * @access private
	 * @var array
	 */
	private $extension = array('jpg','png','gif','zip','rar','avi','wmv','mpg','pdf','doc','docx','xls','txt');
	
	/**
	 * Mode sécurisé ou non (bloquage de tout type jugé dangereux)
	 * @since 1.1.0
	 * @var boolean
	 */
	public $secureMode;
	
	/**
	 * Tableau des fichier uploadé
	 * @since 1.2.1
	 * @var array
	 */
	public $uploadedFile;
	
	/**
	 * Fichier de type mime
	 *
	 * @var string
	 */
	public $finfo;
	
	/**
	 * Constructeur
	 *
	 * @param string $field Nom du champs
	 * @param string $path Dossier d'upload
	 */
	public function __construct($field,$path)
	{
		if(function_exists('finfo_open'))
		{
			$this->maxFileSize 		= str_replace('M', '', ini_get('upload_max_filesize')) * 1024;
			$this->dirUpload   		= $this->checkPath($path);
			$this->secureMode  		= true;
			$this->fileName    		= '';
			$this->prefixe	   		= '';
			$this->suffixe	   		= '';
			$this->uploadedFile		= array();
			$this->fieldName   		= $field;
			$this->required    		= self::REQUIRE_YES;
			$this->writeMode   		= self::WMODE_OVERWRITE;
			$this->finfo			= Config::getFinfo();
			$this->setTimeLimit();
		}
		else
		{
			throw new Util_ExceptionHandler('Cette classe nécessite l\'extension PECL Fileinfo');
		}
	}
	
	/**
	 * Déclenche l'upload des fichiers et tous les traitements associés
	 * @access public
	 * @return boolean
	 */
	public function upload()
	{
		$nbFile = count($_FILES[$this->fieldName]['tmp_name']);

		for($i=0;$i<$nbFile;$i++)
		{
			$this->_taille = $_FILES[$this->fieldName]['size'][$i];
			$this->_nom    = $_FILES[$this->fieldName]['name'][$i];
			$this->_temp   = $_FILES[$this->fieldName]['tmp_name'][$i];
			if(!empty($this->_nom))
				$this->_ext    = File_Util::getExtension($this->_nom);
			$finfo = new finfo(FILEINFO_MIME,$this->finfo); // /!\ Chemin du fichier magic a changer selon environnement
			$this->_mime 	= $finfo->file(realpath($this->_temp)); // Peut retourner des mime du style : "text/plain; charset=utf-8"
			$posVirgule 	= strpos($this->_mime,';');
			if($posVirgule!==FALSE)
			{
				$this->_mime = substr($this->_mime,0,$posVirgule);
			}
			if(is_uploaded_file($_FILES[$this->fieldName]['tmp_name'][$i]))
			{
				if($this->checkSize())
				{
					if($this->checkMime())
					{
						if($this->checkExtension())
						{
							$this->buildName();
							if(!$this->write())
								throw new Util_ExceptionHandler('Une erreur est survenue pendant l\'envoi');
							$this->uploadedFile[] = $this->fileName;
						}
						else
						{
							throw new Util_ExceptionHandler('L\'extension '.$this->_ext.' n\'est pas autorisée');
						}
					}
					else
					{
						throw new Util_ExceptionHandler('Le type mime '.$this->_mime.' n\'est pas autorisé');
					}
				}
				else
				{
					throw new Util_ExceptionHandler('Le fichier '.$this->_nom.' est trop gros ('.$this->maxFileSize.' Ko max)');
				}
				//Vérification
			}
			else
			{
				//$this->addError($_FILES[$this->fieldName]['error'][$i]);
				if($this->required==self::REQUIRE_ALL || $this->required==self::REQUIRE_YES && $i==0)
					throw new Util_ExceptionHandler($_FILES[$this->fieldName]['error'][$i]);
			}
		}
	}
	
	/**
	 * Déplace le fichier uploader dans son répertoire de destination
	 * @access public
	 * @return boolean
	 */
	private function write()
	{
		if($this->writeMode == self::WMODE_OVERWRITE)
		{
			if($this->exist())
				unlink($this->dirUpload.$this->fileName);
			return move_uploaded_file($this->_temp, $this->dirUpload.$this->fileName);
		}
		elseif($this->writeMode == self::WMODE_COPY)
		{
			if($this->exist())
				$this->fileName = 'Copie_de_'.$this->fileName;
			return move_uploaded_file($this->_temp, $this->dirUpload.$this->fileName);
		}
		else
		{
			if(!$this->exist())
				 return move_uploaded_file($this->_temp, $this->dirUpload.$this->fileName);

		}
	}
	
	/**
	 * Ajoute des extensions aux extensions déjà autorisées
	 * @access public
	 * @param mixed $newExt Tableau des nouvelles extensions
	 */
	public function addExtension($newExt)
	{
		if(is_array($newExt))
		{
			foreach($newExt as $value)
			{
				$this->extension[] = $value;
			}
		}
		else
			$this->extension[] = $newExt;
		//print_r($this->extension);
	}
	
	/**
	 * Retire une extension des extensions autorisées
	 * @access public
	 * @param string $extToRm
	 */
	public function rmExtension($extToRm)
	{
		if($cle = array_search($extToRm,$this->extension))
		{
			unset($this->extension[$cle]);
		}
	}
	
	/**
	 * Vide totalement le tableau des extensions autorisées
	 * @access public
	 */
	public function flushExtension()
	{
		unset($this->extension);
	}
	
	/**
	 * Retourne les extension actuellement disponibles
	 * @access public
	 * @return array
	 */
	public function getExtension()
	{
		return $this->extension;
	}
	
	/**
	 * Ajoute un ou plusieurs type mime aux types autorisés
	 * @since 1.1.0
	 * @access public
	 * @param mixed $newMime Type(s) Mime
	 */
	public function addMimeType($newMime)
	{
		if(is_array($newMime))
		{
			foreach($newMime as $value)
			{
				$this->authorizedMime[] = $value;
			}
		}
		else
			$this->authorizedMime[] = $newMime;
	}
	
	/**
	 * Retire un type Mime des mime autorisées
	 * @since 1.1.0
	 * @access public
	 * @param string $mimeToRm
	 */
	public function rmMimeType($mimeToRm)
	{
		if($cle = array_search($mimeToRm,$this->authorizedMime))
		{
			unset($this->authorizedMime[$cle]);
		}
	}
	
	/**
	 * Vide totalement le tableau des mime autorisées
	 * @since 1.1.0
	 * @access public
	 */
	public function flushMimeType()
	{
		unset($this->authorizedMime);
	}
		
	/**
	 * Retourne les mime actuellement disponibles
	 * @since 1.1.0
	 * @access public
	 * @return array
	 */
	public function getMimeType()
	{
		return $this->authorizedMime;
	}
	
	/**
	 * Vérifie l'existance d'un fichier
	 * @access private
	 * @return boolean
	 */
	private function exist()
	{
		if(file_exists($this->dirUpload.$this->fileName))
			return true;
		return false;
	}
	
	/**
	 * Modifie le temps d execution maximal d'un fichier pour php
	 * @author Olivier VEUJOZ
	 * @access private
	 */
	private function setTimeLimit()
	{
		// Le temps calcul est théoriquement le plus rapide => * 2
		@set_time_limit(ceil(ceil($_POST['MAX_FILE_SIZE'] * 8) / (56 * 1000) * count($_FILES) * 2));
	}
	
	/**
	 * Vérifie le dossier d'upload
	 * @access private
	 * @param string $path
	 * @return string
	 */
	private function checkPath($path)
	{
		$path = trim($path);
		if($path[0]=='/')
		{
			echo $path = substr($path,1);
		}
		if(!is_dir($path))
		{
			throw new Util_ExceptionHandler($path.' n\'est pas un répertoire valide');
		}
		if(!is_writable($path))
		{
			throw new Util_ExceptionHandler($path.' n\'est pas ouvert en écriture');
		}
		return $path;
	}
	
	/**
	 * Vérifie la taille du fichier
	 * @access private
	 * @return boolean
	 */
	private function checkSize()
	{	
		if($this->maxFileSize*1024>$this->_taille)
		{
			return true;
		}
		return false;
	}
	
	/**
	 * Vérifie le type mime du fichier.
	 * Modifiée depuis 1.1.0 pour filtrage via liste blanche
	 * @access private
	 * @return boolean
	 */
	private function checkMime()
	{
		if($this->secureMode)
		{
			if(!in_array($this->_mime,$this->authorizedMime) || strpos($this->_mime,'application') || preg_match("/.php$|.php3$|.php5$|.inc$|.js$|.exe$/i", $this->_ext))
				return false;			
		}
		else
		{
			if(!in_array($this->_mime,$this->authorizedMime))
				return false;
		}
		return true;
	}
	
	/**
	 * Vérifie les extensions des fichiers
	 *
	 * @access private
	 * @return boolean
	 */
	private function checkExtension()
	{
		if(in_array($this->_ext,$this->extension))
		{
			return true;	
		}
		return false;
	}
	
	/**
	 * Construit le nom du fichier uploadé si besoin
	 * @access public
	 *
	 */
	private function buildName()
	{
		if($this->fileName=='')
			$this->fileName = substr($this->_nom, 0, strrpos($this->_nom, '.'));
			
		$this->fileName = Util_String::cleantitre($this->fileName);
		$this->fileName = $this->prefixe.$this->fileName.$this->suffixe.'.'.$this->_ext;
	}
}
 ?>